defmodule SockWorkstation.SockCtrlClient do
  use WebSockex

  # WebSocket 服务器地址
  @url "wss://72-j.cn:5179/xiaozhi/ctl?token=ctl-token"
  # 保活间隔（60 秒）
  # @keepalive_interval 60_000
  @keepalive_ping 45_000
  @token "ctl-token"

  alias SockWorkstation.{
    CtrlMgrGen,
    # SockCoopClient,
    # CoopMgrGen,
    # TTSHelperGen,
    # CoopHelperSup
  }

  def start_link(_initial_value) do
    headers = [{"Authorization", "Bearer #{@token}"}, {"Connection", "keep-alive"}]
    WebSockex.start_link(@url, __MODULE__, %{}, extra_headers: headers)
  end

  @impl WebSockex
  def handle_connect(_conn, state) do
    self_pid = self()
    CtrlMgrGen.fetch_ctl_pid(self_pid)
    schedule_ping()
    IO.puts("@@@ Ctl Wss Connected! #{System.system_time(:second)}")

    state =
      state
      |> Map.put(:self_pid, self_pid)

    {:ok, state}
  end

  @impl WebSockex
  def handle_frame({:text, message}, state) do
    msg =
      message |> Jason.decode!()

    # IO.inspect(msg)
    case msg["event"] do
      "start_coop" ->
        session_id = msg["session_id"]
        user_id = msg["user_id"]
        IO.puts("Received start_coop message #{session_id} #{user_id}")
        # CoopHelperSup.start_coop_mgr(session_id)
        # t1 = Task.async(fn -> CoopHelperSup.super_coop_mgr(session_id) end)
        # Task.await(t1)
        #  CtrlMgrGen.start_peer_coop(session_id,nil)
         CtrlMgrGen.start_peer_coop(session_id,user_id)
        # CoopMgrGen.runfirst_for_uid(session_id, nil)

        {:ok, state}

      _ ->
        IO.puts("ctl cli received other text message: #{message}")
        {:ok, state}
    end
  end

  def handle_frame({:binary, _data}, state) do
    {:ok, state}
  end

  @impl WebSockex
  def handle_disconnect(close_map, state) do
    IO.puts("WebSocket disconnected: #{inspect(close_map)}")
    {:reconnect, state}
  end

  @impl WebSockex
  def handle_pong(:pong, state), do: {:ok, state}
  def handle_pong({:pong, _}, state), do: {:ok, state}

  @impl WebSockex
  def handle_info(:ping, state) do
    # IO.puts("ctrl sending ping")
    schedule_ping()
    {:reply, :ping, state}
  end

  def send_text(pid, message) do
    WebSockex.send_frame(pid, {:text, message})
  end

  def send_binary(pid, binary_data) do
    WebSockex.send_frame(pid, {:binary, binary_data})
  end

  defp schedule_ping do
    Process.send_after(self(), :ping, @keepalive_ping)
  end

  # @impl true
  # def terminate(_reason, _state) do
  #   # IO.puts("Ctrl WebSocket client is terminating. Reason: #{inspect(reason)}")
  #   CtrlMgrGen.rm_ctrl_pid()
  #   :ok
  # end
end

# Process.sleep(120)
# coop_mgr = CoopMgrGen.get_state(session_id)

# # if   state.is_inited? == false && coop_mgr.is_inited? == false do
# # && is_nil(user_id) && is_pid(coop_mgr.tts_helper_pid)

# cond do
#   coop_mgr.is_inited? == false && !is_nil(coop_mgr.user_id) ->
#     CoopMgrGen.runfirst_for_uid(session_id, user_id)
#     send(coop_mgr.tts_helper_pid, :run_bcast_authcode)

#   true ->
#     IO.puts("Cop Wss  Connected for #{state.user_id}!")
# end

# if !is_nil(coop_mgr.user_id), do: send(coop_mgr.tts_helper_pid,:run_bcast_authcode)
# end

# def handle_info({:first_coopmgr, session_id, user_id}, state) do
# session_id = state.session_id
# IO.puts("Received start_coop message #{session_id}")
# is_inited = msg["is_inited"]
# user_id = state.user_id
# CoopHelperSup.super_coop_mgr(session_id)
# Process.sleep(120)
# CoopMgrGen.runfirst_for_uid(session_id, user_id)

# if   state.is_inited? == false && coop_mgr.is_inited? == false do
# && is_nil(user_id) && is_pid(coop_mgr.tts_helper_pid)
# Process.sleep(120)
# Task.async(fn -> CoopHelperSup.super_coop_mgr(session_id) end)
#  coop_mgr = CoopMgrGen.get_state(session_id)
# IO.inspect(coop_mgr)

# cond do

#   (coop_mgr.is_inited? == false) && !is_nil(coop_mgr.user_id) ->
#     CoopMgrGen.runfirst_for_uid(session_id, user_id)
#     send(coop_mgr.tts_helper_pid, :run_bcast_authcode)

#   true ->
#     IO.puts("Cop Wss  Connected for #{state.user_id}!")
#  end
#  end
# {:ok, state}
# end

# Process.sleep(120)
# Process.send_after(state.self_pid, {:first_coopmgr, session_id, user_id}, 120)

# # is_inited = msg["is_inited"]

# CoopHelperSup.super_coop_mgr(session_id)
# Process.sleep(240)

# end

# _ ->
#   {:ok, state}
# end

# "recv_broadcast" ->
# IO.puts("ctl cli received broadcast message: #{message}")
#   CoopHelperSup.start_coop_mgr(session_id)
# Process.sleep(1500)
# coop_mgr = CoopMgrGen.get_state(state.session_id)
# send(coop_mgr.coop_pid, {:run_add_coop, coop_mgr.coop_pid})

# if state.session_id,
#   do:
#     send(
#       coop_mgr.tts_helper_pid,
#       {:run_test_opus, "greet_authcode", "1150", "hello-1150"}
#     )

# send(
#   CoopMgrGen.get_state(state.session_id).tts_helper_pid,
#   {:bcast_tts,:authcode, msg["text"]}
# )

# Process.sleep(6000)
# SockCoopClient.send_goodbye(coop_mgr.coop_pid)
# #   # Process.sleep(10000)
# #   # {:reply, {:text, @sent_standby |> Jason.encode!()},state}
# {:ok, state}

# 保活定时器回调
# @impl WebSockex
# def handle_info(:send_keepalive, state) do
#   # 发送保活消息
#   send_text(self(), "keepalive")
#   schedule_ping()
#   # 重新启动保活定时器
#   schedule_keepalive()
#   {:ok, state}
# end

#   CoopHelperSup.start_coop_mgr(session_id)
#   Process.sleep(1500)
#   coop_mgr = CoopMgrGen.get_state(session_id)
#   send(coop_mgr.coop_pid, {:run_add_coop, coop_mgr.coop_pid})
#   if state.session_id,

# case user_id do
#   nil ->
#     {:ok, state}

#   _ ->
# state =
#   state
#   |> Map.put(:session_id, session_id)
#   |> Map.put(:user_id, user_id)

# user_id = msg["user_id"]
# CtrlMgrGen.uid_peer_session(user_id, session_id)
# case CoopHelperSup.start_coop_mgr(session_id) do
#   {:ok, _} ->
#     {:ok, Map.put(state, :session_id, session_id)}

#   # IO.puts("CoopMgrGen start_link ok")
#   {:already_started, _pid} ->
#     {:ok, state}

#   {:error, reason} ->
#     IO.puts("CoopMgrGen start_link error: #{inspect(reason)}")
#     {:ok, state}
# end

#  Process.sleep(2500)
# {:ok, state}

# "broadcast_authcode" ->
#   IO.puts("Received broadcast_authcode message")
#   session_id = msg["session_id"]
# CoopMgrGen.start_link(session_id)

#   CoopHelperSup.start_coop_mgr(session_id)

# defmodule OpusParser do
#   @moduledoc """
#   解析 Opus 帧格式的工具模块。
#   """

#   defp filter_invalid_chars(data) do
#     # 过滤掉所有非二进制数据（如 ASCII 字符）
#     for <<byte <- data>>, byte in 0..255, into: <<>>, do: <<byte>>
#   end

#   @doc """
#   从二进制数据中解析 Opus 帧。

#   返回一个元组：`{:ok, frame, rest}`，其中：
#   - `frame` 是解析出的 Opus 帧。
#   - `rest` 是剩余的数据。

#   如果数据不足以解析一个完整的帧，返回 `:incomplete`。
#   如果 TOC 字节无效，返回 `:invalid_toc`。
#   """
#   def parse_frame(data) do
#     data = filter_invalid_chars(data)

#     case data do
#       <<toc::8, rest::binary>> ->
#         # 解析 TOC 字节
#         config = :erlang.band(toc, 0b11)
#         mode = :erlang.bsr(:erlang.band(toc, 0b1100), 2)
#         frame_length_indicator = :erlang.bsr(:erlang.band(toc, 0b11110000), 4)

#         # 检查 TOC 字节是否有效
#         if frame_length_indicator in 0..15 do
#           # 根据 TOC 字节确定帧长度
#           frame_length = get_frame_length(frame_length_indicator)

#           if byte_size(rest) >= frame_length do
#             # 提取帧数据
#             <<frame::binary-size(frame_length), rest::binary>> = rest
#             {:ok, frame, rest}
#           else
#             # 数据不足以解析一个完整的帧
#             :incomplete
#           end
#         else
#           # TOC 字节无效
#           :invalid_toc
#         end

#       _ ->
#         # 数据不足以解析 TOC 字节
#         :incomplete
#     end
#   end

#   # 根据帧长度指示获取帧长度
#   defp get_frame_length(frame_length_indicator) do
#     case frame_length_indicator do
#       0 -> 1
#       1 -> 2
#       2 -> 2
#       3 -> 2
#       4 -> 4
#       5 -> 4
#       6 -> 4
#       7 -> 4
#       8 -> 8
#       9 -> 8
#       10 -> 8
#       11 -> 8
#       12 -> 16
#       13 -> 16
#       14 -> 16
#       15 -> 16
#     end
#   end
# end

# defmodule OpusParser do
#   @moduledoc """
#   解析 Opus 帧格式的工具模块。
#   """

#   defp filter_invalid_chars(data) do
#     # 过滤掉所有非二进制数据（如 ASCII 字符）
#     for <<byte <- data>>, byte in 0..255, into: <<>>, do: <<byte>>
#   end

#   @doc """
#   从二进制数据中解析 Opus 帧。

#   返回一个元组：`{:ok, frame, rest}`，其中：
#   - `frame` 是解析出的 Opus 帧。
#   - `rest` 是剩余的数据。

#   如果数据不足以解析一个完整的帧，返回 `:incomplete`。
#   如果 TOC 字节无效，返回 `:invalid_toc`。
#   """
#   def parse_frame(data) do
#     data = filter_invalid_chars(data)

#     case data do
#       <<toc::8, rest::binary>> ->
#         # 解析 TOC 字节
#         config = :erlang.band(toc, 0b11)
#         mode = :erlang.bsr(:erlang.band(toc, 0b1100), 2)
#         frame_length_indicator = :erlang.bsr(:erlang.band(toc, 0b11110000), 4)

#         # 检查 TOC 字节是否有效
#         if frame_length_indicator in 0..15 do
#           # 根据 TOC 字节确定帧长度
#           frame_length = get_frame_length(frame_length_indicator)

#           if byte_size(rest) >= frame_length do
#             # 提取帧数据
#             <<frame::binary-size(frame_length), rest::binary>> = rest
#             {:ok, frame, rest}
#           else
#             # 数据不足以解析一个完整的帧
#             :incomplete
#           end
#         else
#           # TOC 字节无效
#           :invalid_toc
#         end

#       _ ->
#         # 数据不足以解析 TOC 字节
#         :incomplete
#     end
#   end

#   # 根据帧长度指示获取帧长度
#   defp get_frame_length(frame_length_indicator) do
#     case frame_length_indicator do
#       0 -> 1
#       1 -> 2
#       2 -> 2
#       3 -> 2
#       4 -> 4
#       5 -> 4
#       6 -> 4
#       7 -> 4
#       8 -> 8
#       9 -> 8
#       10 -> 8
#       11 -> 8
#       12 -> 16
#       13 -> 16
#       14 -> 16
#       15 -> 16
#     end
#   end
# end

# defmodule OpusParser do
#   @moduledoc """
#   解析 Opus 帧格式的工具模块。
#   """

#   defp filter_invalid_chars(data) do
#     # 过滤掉所有非二进制数据（如 ASCII 字符）
#     for <<byte <- data>>, byte in 0..127, into: <<>>, do: <<byte>>
#   end

#   @doc """
#   从二进制数据中解析 Opus 帧。

#   返回一个元组：`{:ok, frame, rest}`，其中：
#   - `frame` 是解析出的 Opus 帧。
#   - `rest` 是剩余的数据。

#   如果数据不足以解析一个完整的帧，返回 `:incomplete`。
#   如果 TOC 字节无效，返回 `:invalid_toc`。
#   """
#   def parse_frame(data) do
#     data = filter_invalid_chars(data)

#     case data do
#       <<toc::8, rest::binary>> ->
#         # 解析 TOC 字节
#         config = :erlang.band(toc, 0b11)
#         mode = :erlang.bsr(:erlang.band(toc, 0b1100), 2)
#         frame_length_indicator = :erlang.bsr(:erlang.band(toc, 0b11110000), 4)

#         # 检查 TOC 字节是否有效
#         if frame_length_indicator in 0..15 do
#           # 根据 TOC 字节确定帧长度
#           frame_length = get_frame_length(frame_length_indicator)

#           if byte_size(rest) >= frame_length do
#             # 提取帧数据
#             <<frame::binary-size(frame_length), rest::binary>> = rest
#             {:ok, frame, rest}
#           else
#             # 数据不足以解析一个完整的帧
#             :incomplete
#           end
#         else
#           # TOC 字节无效
#           :invalid_toc
#         end

#       _ ->
#         # 数据不足以解析 TOC 字节
#         :incomplete
#     end
#   end

#   # 根据帧长度指示获取帧长度
#   defp get_frame_length(frame_length_indicator) do
#     case frame_length_indicator do
#       0 -> 1
#       1 -> 2
#       2 -> 2
#       3 -> 2
#       4 -> 4
#       5 -> 4
#       6 -> 4
#       7 -> 4
#       8 -> 8
#       9 -> 8
#       10 -> 8
#       11 -> 8
#       12 -> 16
#       13 -> 16
#       14 -> 16
#       15 -> 16
#     end
#   end
# end

# defmodule OpusParser do
#   @moduledoc """
#   解析 Opus 帧格式的工具模块。
#   """

# defp filter_invalid_chars(data) do
#   # 过滤掉所有非二进制数据（如 ASCII 字符）
#   for <<byte <- data>>, byte in 0..127, into: <<>>, do: <<byte>>
# end

#   @doc """
#   从二进制数据中解析 Opus 帧。

#   返回一个元组：`{:ok, frame, rest}`，其中：
#   - `frame` 是解析出的 Opus 帧。
#   - `rest` 是剩余的数据。

#   如果数据不足以解析一个完整的帧，返回 `:incomplete`。
#   如果 TOC 字节无效，返回 `:invalid_toc`。
#   """
#   def parse_frame(data) do
#     data=filter_invalid_chars(data)
#     case data do
#       <<toc::8, rest::binary>> ->
#         # 解析 TOC 字节
#         # 使用 Bitwise.band 进行按位与
#         config = toc |> Bitwise.band(0b11)
#         # 使用 Bitwise.bsr 进行按位右移
#         mode = toc |> Bitwise.bsr(2) |> Bitwise.band(0b11)
#         # 使用 Bitwise.band 进行按位与
#         frame_length_indicator = toc |> Bitwise.bsr(4) |> Bitwise.band(0b1111)

#         # 检查 TOC 字节是否有效
#         if frame_length_indicator in 0..15 do
#           # 根据 TOC 字节确定帧长度
#           frame_length = get_frame_length(frame_length_indicator)

#           if byte_size(rest) >= frame_length do
#             # 提取帧数据
#             <<frame::binary-size(frame_length), rest::binary>> = rest
#             {:ok, frame, rest}
#           else
#             # 数据不足以解析一个完整的帧
#             :incomplete
#           end
#         else
#           # TOC 字节无效
#           :invalid_toc
#         end

#       _ ->
#         # 数据不足以解析 TOC 字节
#         :incomplete
#     end
#   end

#   # 根据帧长度指示获取帧长度
#   defp get_frame_length(frame_length_indicator) do
#     case frame_length_indicator do
#       0 -> 1
#       1 -> 2
#       2 -> 2
#       3 -> 2
#       4 -> 4
#       5 -> 4
#       6 -> 4
#       7 -> 4
#       8 -> 8
#       9 -> 8
#       10 -> 8
#       11 -> 8
#       12 -> 16
#       13 -> 16
#       14 -> 16
#       15 -> 16
#     end
#   end
# end

# message =
# message
# |> to_charlist()
# |> Enum.join("X")
# |> to_string()
# |> String.replace("X", "")
# |> to_charlist()
# |> :binary.list_to_bin()
# |> :binary.bin_to_list()
# 过滤掉字符 "X"
# |> Enum.filter(fn byte -> byte != ?X end)
# 转换回二进制
# |> :binary.list_to_bin()

# case OpusParser.parse_frame(message) do
#   {:ok, frame, _rest} ->
#     IO.puts("Parsed frame: #{inspect(frame)}")
#     # save_frame(frame)
#     # IO.puts("Remaining data: #{inspect(rest)}")

#   # port =
#   #   Port.open({:spawn, "ffplay -i pipe:0 -autoexit -ar 24000"}, [:binary])

#   # Port.command(port, frame)
#   :incomplete ->
#     IO.puts("Incomplete frame, need more data")

#   :invalid_toc ->
#     IO.puts("Invalid TOC byte")
# end

# SockWorkstation.OpusServer.decode_opus_data(message)
# receive do
#   {:binary, value} ->
#     IO.inspect(value)
#     # code
# end

# |> receive do
#   {:ok, decoded_data} ->
#     IO.puts("Decoded data:")
#     IO.inspect(decoded_data)
#   end
# |>then(fn x -> IO.inspect(x) end)

# IO.write(:stdio, message)
# :stdio.write(message)
# ffmpeg -i output.opus -f pulse default

# File.write!("raw_audio.opus", message, [:append])

# |>then(fn x -> :stdio.write() end)
#  ffmpeg_command = "ffmpeg -i #{message} -f pulse default"
#   System.cmd("sh", ["-c", ffmpeg_command])
# Port.open({:spawn_executable, "ffmpeg  -i #{message|>to_charlist()|>Enum.join("X")}  -f opus - | ffplay -  "}, [:binary])
# Port.open({:spawn, "ffmpeg  -i #{message}  -f opus - | ffplay -  "}, [:binary])
# port =
#   Port.open({:spawn, "ffmpeg -f s16le -ar 16000 -ac 1 -i pipe:0 -f alsa default"}, [:binary])

# Port.command(port, message)

# SockWorkstation.CoopMgrGen.start_link(msg["session_id"])
# SockWorkstation.CoopMgrGen.run_coop_client(msg["session_id"])
# SockWorkstation.CoopHelperSup.start_coop_client(msg["session_id"], CoopMgrGen.get_state(msg["session_id"]).coop_mgr_pid)

# send(CoopMgrGen.get_state(msg["session_id"]).coop_mgr_pid, :run_sent_asr)
# SockWorkstation.CoopHelperSup.start_opus_decode({msg["session_id"], CoopMgrGen.get_state(msg["session_id"]).coop_pid})
# SockWorkstation.CoopHelperSup.start_pcm_encode({msg["session_id"], CoopMgrGen.get_state(msg["session_id"]).coop_pid})
# SockWorkstation.CoopMgrGen.start_run_asr(msg["session_id"])
# SockWorkstation.SockCoopClient.start_link(msg["session_id"], CoopMgrGen.get_state(msg["session_id"]).coop_mgr_pid)

# SockWorkstation.SockASRClient.start_link({msg["session_id"], CoopMgrGen.get_state(msg["session_id"]).coop_mgr_pid})
# SockWorkstation.CoopHelperSup.start_asr_client(
#   {msg["session_id"], CoopMgrGen.get_state(msg["session_id"]).coop_mgr_pid}
# )
# SockWorkstation.CoopHelperSup.start_coop_client(msg["session_id"])

# asr_pid = SockWorkstation.CoopMgrGen.get_state(msg["session_id"]).asr_pid
# SockWorkstation.SockASRClient.send_run_task(asr_pid, UUID.uuid4())
# SockWorkstation.CoopHelperSup.start_asr_client({msg["session_id"], CoopMgrGen.get_state(msg["session_id"]).coop_mgr_pid})
